package com.sojson.util.file.system.impl;

import java.io.InputStream;
import java.util.Date;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.auth.sts.AssumeRoleResponse.Credentials;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.sojson.constant.Constant;
import com.sojson.util.StringUtil;
import com.sojson.util.ali.AliUtil;
import com.sojson.util.ali.bean.AliConfig;
import com.sojson.util.file.FileUtil;
import com.sojson.util.file.bean.UploadBean;
import com.sojson.util.file.system.FileSystemInterface;
import com.sojson.util.id.IdUtil;
import com.sojson.util.init.InitUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * OSS文件系统工具类
 *
 * @author liu
 * @date 2020-09-02
 */
@Slf4j
public class OssUtilImpl implements FileSystemInterface {

    private static OssUtilImpl ossUtilsImpl;
    private final OSSClientBuilder OSS_CLIENT_BUILDER = new OSSClientBuilder();
    OSS client = getClient();
    /** OSS的服务器的协议 */
    private final String agreement = Constant.PROPERTIES.getProperty("so.oss.agreement");
    /** OSS的服务器地址 */
    private final String ip = Constant.PROPERTIES.getProperty("so.oss.ip");
    /** STS的服务器地址 */
    private final String sts = Constant.PROPERTIES.getProperty("so.sts.ip");
    /** 获取阿里云用户的哪些权限 */
    private final String POLICY = "{\n" + "    \"Version\": \"1\", \n" + "    \"Statement\": [\n" + "        {\n"
        + "            \"Action\": [\n" + "                \"oss:*\"\n" + "            ], \n"
        + "            \"Resource\": [\n" + "                \"acs:oss:*:*:*\" \n" + "            ], \n"
        + "            \"Effect\": \"Allow\"\n" + "        }\n" + "    ]\n" + "}";

    private OssUtilImpl() {
    }

    @Override
    protected void finalize() throws Throwable {
        close();
    }

    /**
     * 文件上传
     *
     * @param filePath  文件位置
     * @param dir       上传目录
     * @return
     * @throws Exception
     */
    @Override
    public UploadBean upload(String filePath, String dir) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = FileUtil.readInputStream(filePath);
            return upload(inputStream, dir, FileUtil.getFileSuffix(filePath));
        } finally {
            FileUtil.closeStream(inputStream);
        }
    }

    /**
     * 文件上传
     *
     * @param fileStream    文件流
     * @param dir           上传目录
     * @param fileSuffix    文件后缀
     * @return
     * @throws Exception
     */
    @Override
    public UploadBean upload(InputStream fileStream, String dir, String fileSuffix) throws Exception {
        String objectName = new StringBuilder(dir).append(IdUtil.uuid()).append(fileSuffix).toString();

        client.putObject(BUCKET_NAME, objectName, fileStream);

        UploadBean upload = new UploadBean();
        upload.setObjectName(objectName);
        return upload;
    }

    /**
     * 文件上传
     *
     * @param fileBytes     文件字节流
     * @param dir           上传目录
     * @param fileSuffix    文件后缀
     * @return
     * @throws Exception
     */
    @Override
    public UploadBean upload(byte[] fileBytes, String dir, String fileSuffix) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = FileUtil.readInputStream(fileBytes);
            return upload(inputStream, dir, fileSuffix);
        } finally {
            FileUtil.closeStream(inputStream);
        }
    }

    /**
     * 下载文件
     *
     * @param objectName    唯一标识
     * @param dir           文件路径
     * @param fileName      文件名
     * @return
     * @throws Exception
     */
    @Override
    public boolean download(String objectName, String dir, String fileName) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = downloadGetStream(objectName);
            FileUtil.write(inputStream, new StringBuilder(FileUtil.THIS_LOCALITY_DIRECTORY_BUCKET_NAME_BASE).append("/")
                .append(dir).append("/").append(fileName).toString());
        } finally {
            FileUtil.closeStream(inputStream);
        }
        return true;
    }

    /**
     * 下载文件
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public InputStream downloadGetStream(String objectName) throws Exception {
        return client.getObject(BUCKET_NAME, objectName).getObjectContent();
    }

    /**
     * 下载文件
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public byte[] downloadGetBytes(String objectName) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = downloadGetStream(objectName);
            return FileUtil.readByes(inputStream);
        } finally {
            FileUtil.closeStream(inputStream);
        }
    }

    /**
     * 删除文件
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public boolean remove(String objectName) throws Exception {
        client.deleteObject(BUCKET_NAME, objectName);
        return true;
    }

    /**
     * 获取文件路径
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public String getUrl(String objectName) throws Exception {
        StringBuilder append = new StringBuilder(agreement);
        append.append(BUCKET_NAME);
        append.append(".");
        append.append(ip);
        append.append("/");
        append.append(objectName);
        return StringUtil.encode(append.toString(), Constant.UTF8);
    }

    /**
     * 获取文件路径(临时路径)
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public String getUrlTemporary(String objectName) throws Exception {
        return getUrlTemporary(objectName, FILE_TIMEOUT);
    }

    /**
     * 获取文件路径(临时路径)
     *
     * @param objectName    唯一标识
     * @param timeout       超时时间(单位: 秒)
     * @return
     * @throws Exception
     */
    @Override
    public String getUrlTemporary(String objectName, int timeout) throws Exception {
        // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。F
        return StringUtil.encode(String.valueOf(client.generatePresignedUrl(BUCKET_NAME, objectName,
            new Date(System.currentTimeMillis() + timeout * 1000L))), Constant.UTF8);
    }

    /**
     * 获取客户端连接
     *
     * @return
     */
    private OSS getClient() {
        AliConfig config = null;
        try {
            config = AliUtil.getConfig();
            if (ip != null) {
                InitUtil.add("当前文件存储系统OSS的IP为: " + ip);
            }
            return new OSSClientBuilder().build(ip, config.getAccessKey(), config.getAccessSecret());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 关闭连接
     *
     * @param ossClient
     */
    void close() {
        log.info("正在关闭Oss");
        if (StringUtil.isNotBlankObject(client)) {
            client.shutdown();
        }
    }

    /**
     * 获取连接(别用!!!,私人资源生成Url最多只有12小时)
     *
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unused")
    private OSS getConnectionByAccessSecurityToken() throws Exception {
        Credentials credentials = getAccessSecurityToken().getCredentials();

        // 用户拿到STS临时凭证后，通过其中的安全令牌（SecurityToken）和临时访问密钥（AccessKeyId和AccessKeySecret）生成OSSClient。
        // 创建OSSClient实例。注意，这里使用到了上一步生成的临时访问凭证（STS访问凭证）。
        return OSS_CLIENT_BUILDER.build(ip, credentials.getAccessKeyId(), credentials.getAccessKeySecret(),
            credentials.getSecurityToken());
    }

    /**
     * 获取临时访问凭证以及临时访问RAM角色的密钥信息(别用!!!,最多只能设置12小时)
     *
     * @return
     * @throws Exception
     */
    private AssumeRoleResponse getAccessSecurityToken() throws Exception {
        AliConfig config = AliUtil.getConfig();
        // 添加endpoint（直接使用STS endpoint，第一个参数留空，无需添加region ID）
        DefaultProfile.addEndpoint("", "Sts", sts);
        // 构造default profile（参数留空，无需添加region ID）
        IClientProfile profile = DefaultProfile.getProfile("", config.getAccessKey(), config.getAccessSecret());
        // 用profile构造client
        DefaultAcsClient client = new DefaultAcsClient(profile);

        final AssumeRoleRequest request = new AssumeRoleRequest();
        request.setSysMethod(MethodType.POST);
        request.setRoleArn(config.getRole());
        request.setRoleSessionName(config.getSessionName());
        // 若policy为空，则用户将获得该角色下所有权限
        request.setPolicy(POLICY);
        // 设置凭证有效时间
        request.setDurationSeconds(60L * 15);

        return client.getAcsResponse(request);
    }

    public static OssUtilImpl getInstances() {
        if (StringUtil.isBlankObject(ossUtilsImpl)) {
            ossUtilsImpl = new OssUtilImpl();
        }
        return ossUtilsImpl;
    }

}